"""
作用域管理

定义依赖注入的作用域类型，控制对象的生命周期。
"""

from abc import ABC, abstractmethod
from typing import Any, Dict, Optional, Callable, TypeVar
import threading
import weakref
from contextlib import contextmanager

T = TypeVar('T')


class Scope(ABC):
    """
    作用域抽象基类
    
    定义对象生命周期管理的接口。
    """
    
    @abstractmethod
    def get(self, key: str, factory: Callable[[], T]) -> T:
        """
        获取或创建对象实例
        
        Args:
            key: 对象的唯一标识
            factory: 对象创建工厂函数
        
        Returns:
            对象实例
        """
        pass
    
    @abstractmethod
    def clear(self) -> None:
        """清理作用域中的所有对象"""
        pass
    
    @abstractmethod
    def remove(self, key: str) -> bool:
        """移除指定的对象"""
        pass


class SingletonScope(Scope):
    """
    单例作用域
    
    在整个应用程序生命周期内只创建一个实例。
    """
    
    def __init__(self):
        self._instances: Dict[str, Any] = {}
        self._lock = threading.RLock()
    
    def get(self, key: str, factory: Callable[[], T]) -> T:
        """获取单例实例"""
        if key in self._instances:
            return self._instances[key]
        
        with self._lock:
            # 双重检查锁定
            if key in self._instances:
                return self._instances[key]
            
            instance = factory()
            self._instances[key] = instance
            return instance
    
    def clear(self) -> None:
        """清理所有单例实例"""
        with self._lock:
            self._instances.clear()
    
    def remove(self, key: str) -> bool:
        """移除指定的单例实例"""
        with self._lock:
            if key in self._instances:
                del self._instances[key]
                return True
            return False
    
    def get_instance_count(self) -> int:
        """获取当前实例数量"""
        return len(self._instances)
    
    def list_instances(self) -> list[str]:
        """列出所有实例的key"""
        return list(self._instances.keys())


class TransientScope(Scope):
    """
    瞬态作用域
    
    每次请求都创建新的实例。
    """
    
    def get(self, key: str, factory: Callable[[], T]) -> T:
        """每次都创建新实例"""
        return factory()
    
    def clear(self) -> None:
        """瞬态作用域无需清理"""
        pass
    
    def remove(self, key: str) -> bool:
        """瞬态作用域无法移除"""
        return False


class RequestScope(Scope):
    """
    请求作用域
    
    在单个请求/上下文中保持单例，跨请求创建新实例。
    """
    
    def __init__(self):
        self._local = threading.local()
    
    def get(self, key: str, factory: Callable[[], T]) -> T:
        """获取请求作用域实例"""
        if not hasattr(self._local, 'instances'):
            self._local.instances = {}
        
        if key in self._local.instances:
            return self._local.instances[key]
        
        instance = factory()
        self._local.instances[key] = instance
        return instance
    
    def clear(self) -> None:
        """清理当前请求的所有实例"""
        if hasattr(self._local, 'instances'):
            self._local.instances.clear()
    
    def remove(self, key: str) -> bool:
        """移除当前请求的指定实例"""
        if hasattr(self._local, 'instances') and key in self._local.instances:
            del self._local.instances[key]
            return True
        return False
    
    @contextmanager
    def request_context(self):
        """请求上下文管理器"""
        try:
            yield
        finally:
            self.clear()


class WeakReferenceScope(Scope):
    """
    弱引用作用域
    
    使用弱引用管理对象，当对象不再被引用时自动清理。
    """
    
    def __init__(self):
        self._instances: Dict[str, weakref.ref] = {}
        self._lock = threading.RLock()
    
    def get(self, key: str, factory: Callable[[], T]) -> T:
        """获取弱引用实例"""
        with self._lock:
            if key in self._instances:
                instance = self._instances[key]()
                if instance is not None:
                    return instance
                else:
                    # 弱引用已失效，移除
                    del self._instances[key]
            
            instance = factory()
            self._instances[key] = weakref.ref(instance, lambda ref: self._cleanup(key))
            return instance
    
    def _cleanup(self, key: str) -> None:
        """清理失效的弱引用"""
        with self._lock:
            if key in self._instances:
                del self._instances[key]
    
    def clear(self) -> None:
        """清理所有弱引用"""
        with self._lock:
            self._instances.clear()
    
    def remove(self, key: str) -> bool:
        """移除指定的弱引用"""
        with self._lock:
            if key in self._instances:
                del self._instances[key]
                return True
            return False


class CachedScope(Scope):
    """
    缓存作用域
    
    带有过期时间的缓存作用域。
    """
    
    def __init__(self, ttl_seconds: float = 300):  # 默认5分钟
        self._instances: Dict[str, tuple[Any, float]] = {}
        self._ttl = ttl_seconds
        self._lock = threading.RLock()
    
    def get(self, key: str, factory: Callable[[], T]) -> T:
        """获取缓存实例"""
        import time
        
        with self._lock:
            current_time = time.time()
            
            if key in self._instances:
                instance, created_time = self._instances[key]
                if current_time - created_time < self._ttl:
                    return instance
                else:
                    # 缓存已过期
                    del self._instances[key]
            
            instance = factory()
            self._instances[key] = (instance, current_time)
            return instance
    
    def clear(self) -> None:
        """清理所有缓存实例"""
        with self._lock:
            self._instances.clear()
    
    def remove(self, key: str) -> bool:
        """移除指定的缓存实例"""
        with self._lock:
            if key in self._instances:
                del self._instances[key]
                return True
            return False
    
    def cleanup_expired(self) -> int:
        """清理过期的缓存实例"""
        import time
        
        with self._lock:
            current_time = time.time()
            expired_keys = []
            
            for key, (instance, created_time) in self._instances.items():
                if current_time - created_time >= self._ttl:
                    expired_keys.append(key)
            
            for key in expired_keys:
                del self._instances[key]
            
            return len(expired_keys)


class ScopeManager:
    """
    作用域管理器
    
    管理不同类型的作用域实例。
    """
    
    def __init__(self):
        self._scopes: Dict[str, Scope] = {}
        self._default_scopes()
    
    def _default_scopes(self) -> None:
        """注册默认作用域"""
        self.register("singleton", SingletonScope())
        self.register("transient", TransientScope())
        self.register("request", RequestScope())
        self.register("weak", WeakReferenceScope())
        self.register("cached", CachedScope())
    
    def register(self, name: str, scope: Scope) -> None:
        """注册作用域"""
        self._scopes[name] = scope
    
    def get(self, name: str) -> Optional[Scope]:
        """获取作用域"""
        return self._scopes.get(name)
    
    def unregister(self, name: str) -> bool:
        """注销作用域"""
        if name in self._scopes:
            del self._scopes[name]
            return True
        return False
    
    def list_scopes(self) -> list[str]:
        """列出所有作用域名称"""
        return list(self._scopes.keys())
    
    def clear_all(self) -> None:
        """清理所有作用域"""
        for scope in self._scopes.values():
            scope.clear()


# 全局作用域管理器
scope_manager = ScopeManager()


# 便捷函数
def get_singleton_scope() -> SingletonScope:
    """获取单例作用域"""
    return scope_manager.get("singleton")


def get_transient_scope() -> TransientScope:
    """获取瞬态作用域"""
    return scope_manager.get("transient")


def get_request_scope() -> RequestScope:
    """获取请求作用域"""
    return scope_manager.get("request")


def get_cached_scope() -> CachedScope:
    """获取缓存作用域"""
    return scope_manager.get("cached")


# 作用域装饰器
class ScopeDecorator:
    """作用域装饰器基类"""
    
    def __init__(self, scope_name: str):
        self.scope_name = scope_name
    
    def __call__(self, cls):
        """装饰类，设置作用域信息"""
        cls._di_scope = self.scope_name
        return cls


def singleton(cls):
    """单例作用域装饰器"""
    return ScopeDecorator("singleton")(cls)


def transient(cls):
    """瞬态作用域装饰器"""
    return ScopeDecorator("transient")(cls)


def request_scoped(cls):
    """请求作用域装饰器"""
    return ScopeDecorator("request")(cls)


def cached(ttl_seconds: float = 300):
    """缓存作用域装饰器"""
    def decorator(cls):
        cls._di_scope = "cached"
        cls._di_scope_config = {"ttl_seconds": ttl_seconds}
        return cls
    return decorator
